#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>



#define L 1
#define G 2
#define P 3
#define E 4

typedef struct 
{
    int type;
    char name[128];
    char data[512];
    int len;
}Mode;


void do_client(int lfd, char *ip, int port);
void list_file(int cfd, Mode *mode);
void get_file(int cfd, Mode *mode);
void put_file(int cfd, Mode *mode);


int main(int argc, const char *argv[])
{
    int lfd;
    int cfd;
    struct sockaddr_in serveraddr;
    struct sockaddr_in clientaddr;
    int client_len;
    pid_t pid;

    if(argc != 3)
    {
	printf("usage:%s serverip port.\n", argv[0]);
	return 0;
    }

    //创建套接字
    if((lfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
	perror("fail to socket:");
	return -1;
    }

    //设置端口复用
    int opt = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    //绑定
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));
    if(bind(lfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
	perror("fail to bind:");
	return -1;
    }

    //监听
    if(listen(lfd, 5) < 0)
    {
	perror("fail to listen:");
	return -1;
    }

    //处理僵尸进程
    signal(SIGCHLD, SIG_IGN);

    while(1)
    {
	bzero(&clientaddr, sizeof(clientaddr));
	client_len = sizeof(clientaddr);
	if((cfd = accept(lfd, (struct sockaddr*)&clientaddr, &client_len)) < 0)
	{
	    perror("fail to accept:");
	    return -1;
	}

	//打印客户端信息
	printf("client ip:%s, port:%d\n",inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

	if((pid = fork()) < 0)
	{
	    perror("fail to fork");
	    return -1;
	}
	else if(pid == 0)
	{
	    //子进程
	    close(lfd);
	    do_client(cfd, inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
	}
	else
	{
	    //父进程
	    close(cfd);
	}
    }

    return 0;
}

//处理客户端信息
void do_client(int cfd, char *ip, int port)
{
    int len;
    Mode mode;

    while(1)
    {
	len = recv(cfd, &mode, sizeof(mode), 0);
	if(len < 0)
	{
	    printf("%s--recv--%s\n", __func__, strerror(errno));
	    exit(0);
	}
	else if(len == 0)
	{
	    printf("client ip:%s port:%d exit \n", ip, port);
	    close(cfd);
	    exit(0);
	}
	else
	{
	    switch(mode.type)
	    {
		case L:
		    list_file(cfd, &mode);
		    break;
		case G:
		    get_file(cfd, &mode);
		    break;
		case P:
		    put_file(cfd, &mode);
		    break;
		default:
		    printf("client send data invalid\n");
	    }
	}	    
    }

}

//服务端文件列表信息
void list_file(int cfd, Mode *mode)
{
    DIR *dir;
    struct dirent *dent;
    if((dir = opendir(".")) == NULL)
    {
	printf("%s--opendir--%s\n", __func__, strerror(errno));
	return;
    }

    bzero(mode->data, sizeof(mode->data));
    while(dent = readdir(dir))
    {
	if((strcmp(dent->d_name, ".") == 0) || (strcmp(dent->d_name, "..") == 0))
	    continue;

	strcat(mode->data, dent->d_name);
	strcat(mode->data, "|");
    }
    closedir(dir);

    if(send(cfd, mode, sizeof(Mode), 0) < 0)
    {
	printf("%s--send--%s\n", __func__, strerror(errno));
    }

    printf("目录清单已经成功发送\n");
}

//从服务器下载文件到客户端
void get_file(int cfd, Mode *mode)
{
    int fd;
    int len;
    bzero(mode->data, sizeof(mode->data));

    //打开相应文件
    if((fd = open(mode->name, O_RDONLY, 0777)) < 0)
    {
	printf("%s--send--%s\n", __func__, strerror(errno));

	mode->type = E;
	strcpy(mode->data, "file not fround");

	if(send(cfd, mode, sizeof(Mode), 0) < 0)
	{
	    printf("%s--send--%s\n", __func__, strerror(errno));
	} 

	return ;
    }

    //文件传送
    while((len = read(fd, mode->data, sizeof(mode->data))) > 0)
    {
	mode->len = len;
	if(send(cfd, mode, sizeof(Mode), 0) < 0)
	{
	    printf("%s--send--%s\n", __func__, strerror(errno));
	    return ;
	}

	bzero(mode->data, sizeof(mode->data));

    }

    //文件传送完成
    bzero(mode->data, sizeof(mode->data));
    mode->type = E;
    strcpy(mode->data, "下载完毕");
    if(send(cfd, mode, sizeof(Mode), 0) < 0)
    {
	printf("%s--send--%s\n", __func__, strerror(errno));
	return ;
    }
    printf("文件传送完成\n");
}

//向服务端上传文件
void put_file(int cfd, Mode *mode)
{   
    int fp;

    //创建文件
    if((fp = open(mode->name, O_CREAT|O_RDWR, 0777)) < 0)
    {
	printf("File can not open to write\n");
	return;
    }

    //接客户端数据
    while(1)
    {
	//向文件中写数据
	if(write(fp, mode->data, mode->len) < 0)
	{
	    printf("%s--write--%s", __func__, strerror(errno));
	    break ;
	}

	if(recv(cfd, mode, sizeof(Mode), 0) < 0)
	{
	    printf("%s--recv--%s", __func__, strerror(errno));
	    break ;
	}

	if(mode->type == E)
	{
	    close(fp);
	    printf("%s\n", mode->data);
	    break;
	}
    }
}
